package utils

import (
	"bytes"
	"compress/gzip"
	"crypto/aes"
	"crypto/cipher"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
)

// SaveToFile 将对象保存到文件：序列化为 JSON -> Gzip 压缩 -> AES 加密 -> 写入文件
func SaveToFile[T any](obj T, filePath string, key, iv []byte) error {
	// 检查 key 和 iv 的长度，AES-128 要求它们为 16 字节
	if len(key) != 16 || len(iv) != 16 {
		return errors.New("对于AES-128，Key和IV必须是16字节长")
	}

	// 将对象序列化为 JSON 字节数组
	jsonBytes, err := json.Marshal(obj)
	if err != nil {
		return fmt.Errorf("序列化对象失败: %v", err)
	}

	// 使用 Gzip 压缩 JSON 数据
	var compressedData bytes.Buffer
	gzipWriter := gzip.NewWriter(&compressedData)
	_, err = gzipWriter.Write(jsonBytes)
	if err != nil {
		return fmt.Errorf("压缩数据失败: %v", err)
	}
	_ = gzipWriter.Close() // 记得关闭以完成压缩

	// 创建 AES 加密对象
	block, err := aes.NewCipher(key)
	if err != nil {
		return fmt.Errorf("创建AES加密对象失败: %v", err)
	}

	// 添加 PKCS7 填充
	compressedBytes := compressedData.Bytes()
	paddedData := addPKCS7Padding(compressedBytes, aes.BlockSize)

	// 创建加密器（AES-CBC 模式）
	encryptedData := make([]byte, len(paddedData))
	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(encryptedData, paddedData)

	// 打开文件以写入加密后的数据
	file, err := os.Create(filePath)
	if err != nil {
		return fmt.Errorf("创建文件失败: %v", err)
	}
	defer func(file *os.File) {
		_ = file.Close()
	}(file) // 确保文件句柄会在函数结束时释放

	// 写入加密后的数据到文件
	_, err = file.Write(encryptedData)
	if err != nil {
		return fmt.Errorf("写入文件失败: %v", err)
	}

	return nil
}

// LoadFromFile 从文件中加载对象：读取文件 -> AES 解密 -> Gzip 解压缩 -> JSON 反序列化
func LoadFromFile[T any](filePath string, key, iv []byte) (T, error) {
	var result T // 用于存储解密和解压缩后的对象

	// 检查 key 和 iv 的长度，AES-128 要求它们为 16 字节
	if len(key) != 16 || len(iv) != 16 {
		return result, errors.New("对于AES-128，Key和IV必须是16字节长")
	}

	// 打开文件
	file, err := os.Open(filePath)
	if err != nil {
		return result, fmt.Errorf("无法打开文件: %v", err)
	}
	defer func(file *os.File) {
		_ = file.Close()
	}(file) // 确保文件句柄会在函数结束时释放

	// 读取文件中的字节数组
	fileData, err := io.ReadAll(file)
	if err != nil {
		return result, fmt.Errorf("读取文件内容失败: %v", err)
	}

	// 创建 AES 加密对象
	block, err := aes.NewCipher(key)
	if err != nil {
		return result, fmt.Errorf("创建AES解密对象失败: %v", err)
	}

	// 解密数据
	if len(fileData)%aes.BlockSize != 0 {
		return result, errors.New("加密数据长度不是 AES 块大小的倍数")
	}
	decryptedData := make([]byte, len(fileData))
	mode := cipher.NewCBCDecrypter(block, iv)
	mode.CryptBlocks(decryptedData, fileData)

	// 移除 PKCS7 填充
	decryptedData, err = removePKCS7Padding(decryptedData)
	if err != nil {
		return result, fmt.Errorf("移除填充失败: %v", err)
	}

	// 解压缩数据
	gzipReader, err := gzip.NewReader(bytes.NewReader(decryptedData))
	if err != nil {
		return result, fmt.Errorf("解压缩数据失败: %v", err)
	}
	defer func(gzipReader *gzip.Reader) {
		_ = gzipReader.Close()
	}(gzipReader)

	// 将解压缩后的数据存储到内存中
	var decompressedData bytes.Buffer
	_, err = io.Copy(&decompressedData, gzipReader)
	if err != nil {
		return result, fmt.Errorf("解压缩数据复制失败: %v", err)
	}

	// 将解压缩后的 JSON 字节数组反序列化为对象
	err = json.Unmarshal(decompressedData.Bytes(), &result)
	if err != nil {
		return result, fmt.Errorf("反序列化数据失败: %v", err)
	}

	return result, nil
}

// removePKCS7Padding 移除 PKCS7 填充
func removePKCS7Padding(data []byte) ([]byte, error) {
	length := len(data)
	if length == 0 {
		return nil, errors.New("解密后的数据为空")
	}
	padding := int(data[length-1])
	if padding > length || padding == 0 {
		return nil, errors.New("移除填充失败，填充值无效")
	}
	return data[:length-padding], nil
}

// addPKCS7Padding 添加 PKCS7 填充
func addPKCS7Padding(data []byte, blockSize int) []byte {
	padding := blockSize - len(data)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(data, padtext...)
}
